//
//  ArcView.m
//  HypERLtrace
//
//  Created by Vladimir Sarpe on 29/09/08.
//  Copyright 2008 __MyCompanyName__. All rights reserved.
//

#import "ArcView.h"


@implementation ArcView

- (id)initWithFrame:(NSRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
		processes = [[NSMutableArray alloc] init];
		procCache = nil;
		full = FALSE;
		highlightView = [[HighlightArcView alloc] init];
		[self addSubview:highlightView];
		highl = FALSE;
		refresh = FALSE;
    }
    return self;
}
		
- (void)makeSquare:(int)pos andDrop:(int)drop isComp:(BOOL)comp andCol:(NSColor*)color{
	NSBezierPath *sq = [[NSBezierPath alloc] init];
	int top = 170-drop;
	int bottom = 160-drop;
	[sq setLineWidth:1.0];
	[sq moveToPoint:NSMakePoint(pos, bottom)];
	[sq lineToPoint:NSMakePoint(pos+9, bottom)];
	[sq lineToPoint:NSMakePoint(pos+9, top)];
	[sq lineToPoint:NSMakePoint(pos, top)];
	[sq closePath];
	[color set];
	[sq fill];
	if (comp) {
		NSBezierPath *plus = [[NSBezierPath alloc] init];
		[plus setLineWidth:2.0];

		[plus moveToPoint:NSMakePoint(pos+5, top-2)];
		[plus lineToPoint:NSMakePoint(pos+5, bottom+2)];
		[plus moveToPoint:NSMakePoint(pos+2, top-5)];
		[plus lineToPoint:NSMakePoint(pos+9-1, top-5)];
		[[NSColor redColor] set];
		[plus stroke];
	}
}

- (NSBezierPath*)drawArcFromSquare:(double) first toSquare:(double) second withColor:(NSColor*) color {
	NSBezierPath *arc = [[NSBezierPath alloc] init];
	double firstPoint;
	double secondPoint;
	if (first < second) {
		firstPoint = (first*10)+5;
		secondPoint = (second*10)+5;
	}
	else {
		firstPoint = (second*10)+5;
		secondPoint = (first*10)+5;
	}
	[arc removeAllPoints];
	[arc appendBezierPathWithArcWithCenter:NSMakePoint(firstPoint+(secondPoint-firstPoint)/2, 170) radius:(secondPoint-firstPoint)/2 startAngle:0 endAngle:180];	
	[color set];
	[arc stroke];
	return arc;
}

- (void)buildTreeFromNode:(NSDictionary*)node withLevel:(int)level isTop:(BOOL)top withFit:(float)fit {
	float fitness = 0.0;
	NSString* lifetime = [[node objectForKey:@"vals"] objectForKey:@"lifetime"];
	int lifespan = 0;
	float lifefit = 0.0;
	if (lifetime != nil) {
		NSString* spawn;
		NSString* die;
		NSScanner* scanner = [NSScanner scannerWithString:lifetime];
		[scanner scanUpToString:@"-" intoString:&spawn];
		if ([scanner isAtEnd]) 
			die = [NSString stringWithFormat:@"%d", lastevent];
		else {
			[scanner setScanLocation:[scanner scanLocation] +1];
			[scanner scanUpToString:@" " intoString:&die];
		}
		lifespan = [die intValue] - [spawn intValue];
	}
	else
		lifespan = lastevent;
	lifefit = ((float)lifespan / (float)lastevent) * 10000.0;
	if (top) {
		int sends = [[node objectForKey:@"Sends"] intValue];
		int receives = [[node objectForKey:@"Receives"] intValue];
		int spawns = [[node objectForKey:@"Spawns"] intValue];
		fitness = (float)(1000*sends + receives*500 + spawns*5000 + count + lifefit);
	}
	else {
		fitness = fit - (0.01 * (float)level + 0.001 * (float)count);
	}
	
	NSArray* keys = [NSArray arrayWithObjects:@"process", @"arcto", @"level", @"fitness", nil];
	NSArray* objects = [NSArray arrayWithObjects:[node objectForKey:@"Process"], [[NSMutableArray alloc] init], [NSNumber numberWithInt:level], [NSNumber numberWithFloat:fitness], nil];
	NSDictionary* process = [NSDictionary dictionaryWithObjects:objects forKeys:keys];
	//if (top)
	[processes addObject:process];
	count += 1;
	for (NSDictionary* child in [node objectForKey:@"child"]) {
		[self buildTreeFromNode:child withLevel:level+2 isTop:FALSE withFit:fitness];
	}
}

- (void)loadActors:(NSString*)args {
	/*if ([args isEqualToString:@"seq"]) {
		for (NSString* actor in actors) {
				NSArray* keys = [NSArray arrayWithObjects:@"process", @"arcto", @"level", @"fitness", @"ref", nil];
				NSArray* objects = [NSArray arrayWithObjects:actor, [[NSMutableArray alloc] init], [NSNumber numberWithInt:0], [NSNumber numberWithInt:0], [[NSMutableArray alloc] init], nil];
				NSDictionary* process = [NSDictionary dictionaryWithObjects:objects forKeys:keys];
				[processes addObject:process];
		}
	}
	else*/ if ([args isEqualToString:@"fam"]) {
		for (NSDictionary* node in procTree) {
			[self buildTreeFromNode:node withLevel:0 isTop:TRUE withFit:0.0];
		}
	}
}

-(void) makeArcs {
	for (NSDictionary* event in events) {
		if ([[event objectForKey:@"type"] isEqualToString:@"send"]) {
			NSString* sender = [event objectForKey:@"process"];
			NSString* receiver = [event objectForKey:@"receiver"];
			NSDictionary* senderSquare = nil;
			NSString* receiverSquare = nil;
			NSMutableArray* senderArcs;
			int i;
			for (i = 0; i < [processes count]; i++) {
				//if (i*10 < width) {
				//if (![sender isEqualToString:@"other"] && ![[[processes objectAtIndex:i] objectForKey:@"process"] isEqualToString:@"other"]) {
					NSDictionary* process = [processes objectAtIndex:i];
					if ([[process objectForKey:@"process"] isEqualToString:sender]) {
						senderSquare = process;
						senderArcs = [process objectForKey:@"arcto"];
					}
					if ([[process objectForKey:@"process"] isEqualToString:receiver])
						receiverSquare = receiver;
					if (senderSquare != nil && receiverSquare != nil) {
						break;
					}
				//}
				//}
			}
			if (senderSquare != nil && receiverSquare != nil) {
				//int j = i;
				if (!(i>=(width/10)))
					//j = (width/10)-1;
					[senderArcs addObject:[NSNumber numberWithInt:i]];
			}
		}
	}
}


NSInteger reverseSort(id first, id second, void* context) {
	float v1 = [[first objectForKey:@"fitness"] floatValue] - [[first objectForKey:@"level"] floatValue];
	float v2 = [[second objectForKey:@"fitness"] floatValue] - [[first objectForKey:@"level"] floatValue];
	if (v1 > v2)
        return NSOrderedAscending;
    else if (v1 < v2)
        return NSOrderedDescending;
    else
        return NSOrderedSame;
}



- (void)setActors:(NSDictionary*)actorHash andEvents:(NSArray*)eventTable andTree:(NSArray*)tree andResWidth:(float)scwidth andResHeight:(float)scheight andLast:(int)lastev andArgs:(NSString*)args{
	events = eventTable;
	//actors = [actorHash allKeys];
	width = scwidth;
	height = scheight;
	procTree = tree;
	processes = [[NSMutableArray alloc] init];
	procCache = nil;
	viewType = args;
	count = 0;
	lastevent = lastev;
	/*for (NSString* o in actors) {
		NSLog(o);
	}*/
	[self loadActors:args];	

	NSMutableArray* sortedArray = [[processes sortedArrayUsingFunction:reverseSort context:NULL] mutableCopy];
	 
	processes = sortedArray;
	
	
	NSMutableArray* procs = [processes copy];
	
	
	int prevlevel = 0;
	BOOL removed;
	for (NSDictionary* proc in procs) {
		int level = [[proc objectForKey:@"level"] intValue];
		if (level > 0) {
			if ((prevlevel  == level)) {
				[processes removeObject:proc];
				removed = TRUE;
			}
			else
				removed = FALSE;
		}
		prevlevel = level;
	}
	
	/*for (NSDictionary* proc in processes) {
		NSLog(@" id:%@... fit:%f", [proc objectForKey:@"process"], [[proc objectForKey:@"fitness"] floatValue]);
	}*/

	[self makeArcs];

	/*// -- 

	NSMutableArray* tmp = [processes copy];
	NSDictionary* prev = [tmp objectAtIndex:0];
	int x = 1;
	while (x < [tmp count]) {
		if (([[prev objectForKey:@"level"] intValue] == 0) && ([[[tmp objectAtIndex:x] objectForKey:@"level"] intValue] > 0)) {
			int y = x;
			while ([[[tmp objectAtIndex:y] objectForKey:@"level"] intValue] > 0)
				y++;
			int range = y-x;
			if (range > 0) {
				NSNumber* c_fitness = [[tmp objectAtIndex:x] objectForKey:@"fitness"];
				int* children[range];
				int z;
				NSMutableArray* c_arcto = [[NSMutableArray alloc] init];
				for (z = x; z < y; z++) {
					[c_arcto addObjectsFromArray:[[tmp objectAtIndex:z] objectForKey:@"arcto"]];
					int c_index = z - x;
					children[c_index] = z;
				}
				NSArray* c_keys = [NSArray arrayWithObjects:@"process", @"arcto", @"level", @"fitness", nil];
				NSArray* c_objects = [NSArray arrayWithObjects:@"children", c_arcto, [NSNumber numberWithInt:1], c_fitness, nil];
				NSDictionary* c_rest = [NSDictionary dictionaryWithObjects:c_objects forKeys:c_keys];
				[processes removeObjectsFromIndices:children numIndices:range];
				[processes addObject:c_rest];
			}
			x = y+1;
			prev = [tmp objectAtIndex:y];
		}
	}
	*/// -- 
		
	if ([processes count] >= (width/10)) {
		NSMutableArray* arcto = [[NSMutableArray alloc] init];
		int e = [processes count]-((width/10)-1);
		int i;
		int* b[e];
		for (i = ((width/10)-1); i < [processes count]; i++) {
			[arcto addObjectsFromArray:[[processes objectAtIndex:i] objectForKey:@"arcto"]];
			int index = i - ((width/10)-1);
			b[index] = i;
		}
		NSArray* keys = [NSArray arrayWithObjects:@"process", @"arcto", @"level", @"fitness", nil];
		NSArray* objects = [NSArray arrayWithObjects:@"other", arcto, [NSNumber numberWithInt:0], [NSNumber numberWithFloat:-1.0], nil];
		NSDictionary* rest = [NSDictionary dictionaryWithObjects:objects forKeys:keys];
		[processes removeObjectsFromIndices:b numIndices:e];
		[processes addObject:rest];
	}

	
	// ---
}

- (void)notifyToggle:(NSString*)args {
	if (![viewType isEqualToString:args]) {
		if (procCache == nil) {
			procCache = processes;
			processes = [[NSMutableArray alloc] init];
			[self loadActors:args];
			[self makeArcs];
		}
		else {
			NSMutableArray* tmp = processes;
			processes = procCache;
			procCache = tmp;
		}
		viewType = args;
		[self setNeedsDisplay:TRUE];
	}
}

-(void) labelAtIndex:(int)i withColor:(NSColor*) color{
	
	NSTextStorage *textStorage;
	NSLayoutManager *layoutManager;
	NSTextContainer *textContainer;
	NSString* name;
	
	name = [[processes objectAtIndex:i] objectForKey:@"process"];
	textStorage = [[NSTextStorage alloc] initWithString:name];
	[textStorage setForegroundColor: color];
	layoutManager = [[NSLayoutManager alloc] init];
	textContainer = [[NSTextContainer alloc] init];
	[layoutManager addTextContainer:textContainer];
	[textContainer release];
	[textStorage addLayoutManager:layoutManager];
	[layoutManager release];
	[layoutManager setUsesScreenFonts:NO];
	
	NSRange glyphRange = [layoutManager glyphRangeForTextContainer:textContainer];
	NSGraphicsContext *context = [NSGraphicsContext currentContext];
	NSAffineTransform *transform = [NSAffineTransform transform];
	
	[transform rotateByDegrees:-90.0];
	[context saveGraphicsState];
	[transform concat];
	[self lockFocus];
	[layoutManager drawGlyphsForGlyphRange:glyphRange atPoint:NSMakePoint(-158,(i-1)*10)];
	[self unlockFocus];
	[context restoreGraphicsState];
	
}	

- (void)drawRect:(NSRect)rect {
	//if (!full) {
		[[NSColor blackColor] set];
		NSRectFill(rect);
		int i;
		for (i = 0; i < [processes count]; i++) {
			if (i*10 <= width) {
				BOOL comp = FALSE;
				if (([[[processes objectAtIndex:i] objectForKey:@"process"] isEqualToString:@"other"]))// || ([[[processes objectAtIndex:i] objectForKey:@"level"] intValue] > 0))
					comp = TRUE;
				[self makeSquare:i*10 andDrop:[[[processes objectAtIndex:i] objectForKey:@"level"] intValue] isComp:comp andCol:[NSColor whiteColor]];
						
				NSArray* arcs = [[processes objectAtIndex:i] objectForKey:@"arcto"];
				for (NSNumber* sq in arcs) {
					[self drawArcFromSquare:i toSquare:[sq intValue] withColor:[NSColor colorWithCalibratedWhite:1 alpha:0.3]];
				}
			}
			[self labelAtIndex:i withColor:[NSColor whiteColor]];
		}
		full = TRUE;
		/*NSBitmapImageRep* cached = [self bitmapImageRepForCachingDisplayInRect:[self bounds]];
		[self cacheDisplayInRect:[self bounds] toBitmapImageRep:cached];
		NSData* img = [cached TIFFRepresentation];
		image = [[NSImage alloc] initWithData:img];
		NSLog(@"img length : %d", [img length]);*/
	/*}
	else {
		NSLog(@"image is %f x %f", [image size].width, [image size].height);
		[image drawInRect:[self bounds] fromRect:NSMakeRect(0,0,[image size].width, [image size].height) operation:NSCompositeCopy fraction:1.0];
	}
	//NSRectFill([self bounds]);*/

	if (highl) {
		//[[NSColor blackColor] set];
		//NSRectFill(rect);

		int pos = [[highlight objectForKey:@"pos"] intValue];
		//NSLog(@"%d", pos);
		[[NSColor redColor] set];
		[self makeSquare:pos*10 andDrop:[[[highlight objectForKey:@"info"] objectForKey:@"level"] intValue] isComp:FALSE andCol:[NSColor redColor]];
		
		[self labelAtIndex:pos withColor:[NSColor redColor]];
		for (NSNumber* arc in [[highlight objectForKey:@"info"] objectForKey:@"arcto"]) {
			[self drawArcFromSquare:pos	toSquare:[arc intValue] withColor:[NSColor colorWithCalibratedRed:1.0 green:0.0 blue:0.0 alpha:0.3]];
		}
	}
	
}

-(void)mouseMoved:(NSEvent*)theEvent {
	NSPoint mouseLoc = [self convertPoint:[theEvent locationInWindow] fromView:nil];
	if (mouseLoc.y > 60 && mouseLoc.y <= 170 && mouseLoc.x >= 0 && mouseLoc.x < width) {
		int sq;
		sq = mouseLoc.x/10;
		if (sq < [processes count]) {
			NSDictionary* proc = [processes objectAtIndex:sq];
			NSArray* objects = [NSArray arrayWithObjects:[NSNumber numberWithInt:sq], proc, nil];
			NSArray* keys = [NSArray arrayWithObjects:@"pos", @"info", nil];
			highlight = [NSDictionary dictionaryWithObjects:objects forKeys:keys];
			highl = TRUE;
			[self setNeedsDisplay:TRUE];
			refresh = FALSE;
			//NSLog(@"%d", [[highlight objectForKey:@"pos"] intValue]);
			[self drawRect:[self bounds]];

		}
		else {
			highlight = nil;
			highl = FALSE;
			if (!refresh) {
				[self drawRect:[self bounds]];
			}			
			refresh = TRUE;
			//full = FALSE;
		}
	}
	else {
		highlight = nil;
		highl = FALSE;
		if (!refresh) {
			[self drawRect:[self bounds]];
		}
		refresh = TRUE;
		//full = FALSE;
	}
	//[highlightView setNeedsDisplay:TRUE];
	if (!refresh) {
		[self drawRect:[self bounds]];
		refresh = TRUE;
	}
	//highl = TRUE;
}

-(void)notifyScreen:(BOOL)f {
	full = f;
}

- (BOOL)acceptsFirstResponder{
	return YES;
}



@end
